#pragma once

#include <mathimf.h> // Replaces math functions from std: with Intel versions.

#include <armadillo>
typedef arma::cx_double complex;

#ifdef GRAPHENETOOLS_EXPORTS
#ifndef GRAPHENETOOLS_DLL_API
#define GRAPHENETOOLS_DLL_API __declspec(dllexport)
#endif
#else
#ifndef GRAPHENETOOLS_DLL_API
#define GRAPHENETOOLS_DLL_API __declspec(dllimport)
#endif
#endif

template <typename T>
struct numericrange
{
	T begin;	// This contains the begin numeric.
	T end;	// This contains the end numeric.
	T step;	// This contains the step numeric.

	size_t size() { return size_t((end-begin)/step)+1; }
};

// This class holds a numeric type, implicitly converts back and forth between numeric, size_t, long, double, and complex. If no type has been set the numeric is empty.
class GRAPHENETOOLS_DLL_API numeric
{
public:
	// A type to determine the stored value.
	enum type {
		empty,		// Empty type specifier
		sizetype,	// Size type specifier
		longtype,	// Long type specifier
		doubletype,	// double type specifier
		complextype	// complex type specifier
	};
	
	typedef std::vector<numeric> list;

private:
	type valuetype; // A container for the type.

	union {
		size_t sizevalue;						// A size_t container for the value.
		long longvalue;							// A long container for the value.
		double doublevalue;						// A double container for the value.
		struct { double real, imag; } cxvalue;	// A complex container for the value.
	};

public:
	numeric();					// Default constructor declares an empty type
	numeric(const numeric &n);	// Copy constructor declares a type identical to the numeric n.
	numeric(const size_t &n);	// Conversion constructor declares an size_t type
	numeric(const long &n);		// Conversion constructor declares an long type
	numeric(const double &n);	// Conversion constructor declares an double type
	numeric(const complex &n);	// Conversion constructor declares an complex type

	numeric &operator=(const numeric &n); // Assignment operator declares a type identical to the numeric n.
	
	type getType() const;		// Type getter function.
	bool isEmpty() const;		// Tests for empty type, same as getType() == numeric::empty.
	bool isSizeType() const;	// Tests for size_t type, same as getType() == numeric::sizetype.
	bool isLongType() const;	// Tests for long type, same as getType() == numeric::longtype.
	bool isDoubleType() const;	// Tests for double type, same as getType() == numeric::doubletype.
	bool isComplexType() const;	// Tests for complex type, same as getType() == numeric::complextype.
	
	size_t toSize() const;		// Explicit conversion to size_t function.
	long toLong() const;		// Explicit conversion long function.
	double toDouble() const;	// Explicit conversion double function.
	complex toComplex() const;	// Explicit conversion complex function.
	
	operator size_t() const;	// Implicit conversion to size_t function.
	operator long() const;		// Implicit conversion to long function.
	operator double() const;	// Implicit conversion to double function.
	operator complex() const;	// Implicit conversion to complex function.
	
	numeric &operator+=(const numeric &n);		// numeric relation operator, mimics that of ordinary numerical types.
	numeric &operator-=(const numeric &n);		// numeric relation operator, mimics that of ordinary numerical types.
	numeric &operator*=(const numeric &n);		// numeric relation operator, mimics that of ordinary numerical types.
	numeric &operator/=(const numeric &n);		// numeric relation operator, mimics that of ordinary numerical types.
	numeric operator+(const numeric &n) const;	// numeric relation operator, mimics that of ordinary numerical types.
	numeric operator-(const numeric &n) const;	// numeric relation operator, mimics that of ordinary numerical types.
	numeric operator*(const numeric &n) const;	// numeric relation operator, mimics that of ordinary numerical types.
	numeric operator/(const numeric &n) const;	// numeric relation operator, mimics that of ordinary numerical types.

	// This convenience structure contains three numerics; begin, end, and step. Two static named constructors are provided; interval(begin,end,size) and range(begin,end,step).
	typedef numericrange<numeric> range;
	
	static range interval(const numeric &b, const numeric &e, const size_t s = 2);	// Named constructor that returns a range with begin and end as provided and a step calculated from the size provided.
	static range span(const numeric &b, const numeric &e, const numeric s = (size_t)1);		// Named constructor that returns a range with begin, end and step as provided.
};